﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WcfGeebaServer.Users;
using WcfGeebaServer.Policies;
using WcfGeebaServer.DBDAL;
using WcfGeebaServer.Logging;
using WcfGeebaServer.Utils;

namespace WcfGeebaServer.ContentManage
{
    [Serializable]
    public class GForum
    {
        private static GForum _theForum;

        #region Fields

        private int _guestID; // will hold the current running number of the guests
        private Member admin;
        private GPolicy policy;
        private List<Member> loggedMembers;
        private List<User> currGuests;

        #endregion 

        #region dataMembers

        public GPolicy Policy
        {
            get { return policy; }
        }

        public Member Admin
        {
            get { return admin; }
            private set { admin = value; }
        }

        public List<Member> LoggedMembers 
        {
            get
            {
                return loggedMembers;
            }
        }
        
        public List<User> CurrGuests 
        {
            get
            {
                return currGuests;
            }
        }

        #endregion

        #region Ctors

        private GForum()
        {
            this._guestID = 0;
            this.loggedMembers = new List<Member>();
            this.currGuests = new List<User>();
        }

        #endregion

        #region Methods

        public bool addSubForum(string topic, Member moderator)
        {

            GSubForum subforum = new GSubForum(topic);
            bool res = SubForumDA.GetSubForumDA().AddSubForum(subforum);
            if (res)
            {
                GLogger.log("SubForum added: " + subforum.Topic);
            }
            else
            {
                GLogger.error("Problem adding SubForum: " + subforum.Topic, null);
                return false;
            }
            res = subforum.addInitialMemberAsModerator(moderator);
            if (!res)
            {
                SubForumDA.GetSubForumDA().RemoveSubForum(subforum);
            }
            return res;
        }

        public List<GSubForum> getSubForums()
        {
            return SubForumDA.GetSubForumDA().GetSubForums();
        }

        public bool removeSubForum(GSubForum subforum) 
        {
            bool res = true;
            List<GThread> threads = subforum.getThreads();
            foreach (GThread thread in threads)
            {
                res &= subforum.RemoveThread(thread);
            }
            List<KeyValuePair<Member, ModeratorInfo>> moderators = subforum.getModerators();
            res &= subforum.removeModerators();
            if (res)
            {
                res = SubForumDA.GetSubForumDA().RemoveSubForum(subforum);
                if (res)
                    GLogger.log("Removed SubForum: " + subforum.Topic);
                else
                    GLogger.error("Problem removing SubForum: " + subforum.Topic, null);
                return res;
            }
            return false;
        }

        public bool removeSubForum(string topic)
        {
            return this.removeSubForum(SubForumDA.GetSubForumDA().GetSubForum(topic));
        }

        public bool init(Member admin, GPolicy policy)
        {
            if (policy == null)
            {
                throw new Exception("Policy must not be null!");
            }
            Data.ClearDB();
            //Data.init(); //TODO: harmful!
            GLogger.log("Initializing Forum...");
            MemberDA mda = MemberDA.getMemberDA();
            if (!this.register(admin.Username, admin.Password))
            {
                return false;
            }
            bool res = mda.setInitialAdministrator(admin);
            if (res)
            {
                GLogger.log("Member " + admin.Username + " was set as Administrator");
                this.admin = admin;
                this.policy = policy;
            }
            else
            {
                GLogger.error("Problem setting member " + admin.Username + " as Administrator", null);
            }
            return res;
        }

        public bool register(string username, string password)
        {
            MemberDA mda = MemberDA.getMemberDA();
            if ((mda.GetMember(username) != null) || username.ToLower().StartsWith("guest"))
            {
                return false;
            }
            bool res = mda.AddMember(username, password);
            if (res)
            {
                GLogger.log("Member " + username + " registered successfuly");
            }
            else
            {
                GLogger.error("Problem registering member " + username, null);
            }
            return res;
        }

        public Member login(User user, string username, string pass) 
        {
            if (user == null || !this.CurrGuests.Contains(user))
            {
                throw new Exception("Can't login a user that is not currently a guest!");
            }
            Member m = MemberDA.getMemberDA().GetMember(username);
            if ((m == null) || (!m.Password.Equals(pass)) || m.IsBanned)
            {
                return null;
            }
            this.currGuests.Remove(user);
            this.loggedMembers.Add(m);
            GLogger.log("Member " + username + " logged in");
            return m;
        }

        public bool changeMemberPassword(string requesting, string oldPass, string newPass)
        {
            Member member = this.GetMember(requesting);
            if (member.Password.Equals(oldPass))
            {
                bool res = MemberDA.getMemberDA().changeMemberPassword(member, newPass);
                if (res)
                {
                    GLogger.log("Member " + requesting + " has changed his password");
                }
                else
                {
                    GLogger.error("Problem changing member's " + requesting + " password", null);
                }
                return res;
            }
            return false;

        }

        /// <summary>
        /// generate a unique anonymous username for non-member
        /// </summary>
        /// <returns>unique user</returns>
        public User entry()
        {
            string username = "Guest" + this._guestID;
            this._guestID++;

            User newUser = new User(username);
            this.currGuests.Add(newUser);

            GLogger.log("Guest " + username + " has entered the forum");
            return newUser;
        }

        /// <summary>
        /// if the member is logged in, removes member from logged
        /// in list and returns a newly generated string with its
        /// temporary guest name.
        /// if not logged in returns null.
        /// </summary>
        /// <returns>unique username, or null</returns>
        public User logout(Member member)
        {
            if (!this.loggedMembers.Contains(member))
            {
                throw new Exception("Can't log out a user not currently logged in");
            }
            this.loggedMembers.Remove(member);
            GLogger.log("Member " + member.Username + " has logged out - reentering as guest");
            return this.entry();
        }

        public List<GThread> getThreads(GSubForum subforum)
        {
            return SubForumDA.GetSubForumDA().GetThreads(subforum);
        }

        public bool replaceAdmin(Member newAdmin)
        {
            if (newAdmin == null || !this.policy.CanReplaceAdmin(this.Admin, newAdmin))
                return false;
            bool res = MemberDA.getMemberDA().replaceAdministrator(newAdmin);
            if (res)
            {
                if (admin == null)
                {
                    GLogger.log(newAdmin.Username + " was set as the new Administrator");
                }
                else
                {
                    GLogger.log("The administrator " + this.admin.Username + " was replaced by " + newAdmin.Username);
                }
                admin = newAdmin;
            }
            else
            {
                GLogger.error("Problem replacing administrator " + admin.Username + " by " + newAdmin.Username, null);
            }
            return res;
        }

        public int GetThreadCount(string memberUsername)
        {
            return MemberDA.getMemberDA().GetMember(memberUsername).ThreadCount;
        }

        public Member GetMember(string username)
        {
            return MemberDA.getMemberDA().GetMember(username);
        }

        public GSubForum getSubForum(string topic)
        {
            return SubForumDA.GetSubForumDA().GetSubForum(topic);
        }

        public bool IsMember(string username)
        {
            Member mem = this.GetMember(username);
            return (mem != null);
        }

        public int getConnectedMembersCount()
        {
            return loggedMembers.Count;
        }

        public List<GThread> searchByContent(string content)
        {
            return ThreadDA.GetThreadDA().findThreadsByContent(content);
        }

        public List<GThread> searchByUser(string username)
        {
            return ThreadDA.GetThreadDA().findThreadsByUser(username);
        }

        #endregion

        public static GForum GetForum()
        {
            if (GForum._theForum == null)
            {
                GForum._theForum = new GForum();
                Member admin = new Member("admin", EncDec.Encrypt("admin", EncDec.getServerKey()), true);
                GForum._theForum.init(admin, new GPolicy());

                GForum._theForum.addSubForum("London 2012", admin);
                GForum._theForum.addSubForum("News", admin);
                GForum._theForum.addSubForum("Cars", admin);
                GForum._theForum.addSubForum("Planes", admin);

                GForum._theForum.register("gili",  EncDec.Encrypt("12345678", EncDec.getServerKey()));
                GForum._theForum.register("elior", EncDec.Encrypt("12345678", EncDec.getServerKey()));
                GForum._theForum.register("eyal",  EncDec.Encrypt("12345678", EncDec.getServerKey()));
                GForum._theForum.register("boris", EncDec.Encrypt("12345678", EncDec.getServerKey()));
                GForum._theForum.register("alex",  EncDec.Encrypt("12345678", EncDec.getServerKey()));

                Member gili = new Member("gili", EncDec.Encrypt("12345678", EncDec.getServerKey()), true);
                Member elior = new Member("elior", EncDec.Encrypt("12345678", EncDec.getServerKey()), true);
                Member eyal = new Member("eyal", EncDec.Encrypt("12345678", EncDec.getServerKey()), true);
                Member boris = new Member("boris", EncDec.Encrypt("12345678", EncDec.getServerKey()), true);
                Member alex = new Member("alex", EncDec.Encrypt("12345678", EncDec.getServerKey()), true);


                GForum._theForum.getSubForum("London 2012").publish(new GThread("Israelis on London", "The medal dream is slowly fading away. Alex Shatilov finished 4th place.", gili));
                GForum._theForum.getSubForum("London 2012").publish(new GThread("Swimming", "Olympics day 11 starts today, and our man in London says COOL!!!", gili));
                GForum._theForum.getSubForum("London 2012").publish(new GThread("BasketBall", "boring...", gili));
                GForum._theForum.getSubForum("London 2012").publish(new GThread("Tennis", "Federer wins after the longest tennis game ever in olympic games.", eyal));
                GForum._theForum.getSubForum("London 2012").publish(new GThread("Sailing", "Lee curzis is still at 2nd place after 10... ", alex));
                GForum._theForum.getSubForum("London 2012").publish(new GThread("Athletics", "I cant believe he lost his shoes... ;) probably was loosing his home work also...", alex));
                GForum._theForum.getSubForum("London 2012").publish(new GThread("Running", "Bolt is still the greatest runner of all times. yo yo ...", elior));
                
                GThread gossipThread =  new GThread("GossipGirl", "Elior was seen walking outside Bet Hastudent with a new girl...", elior);
                int t_tid = GForum._theForum.getSubForum("News").publish(gossipThread);
                t_tid = GForum._theForum.getSubForum("News").GetThread(t_tid).addPost(new GPost("GossipGirl reply", "Like!", alex, gossipThread));
                t_tid = GForum._theForum.getSubForum("News").GetThread(t_tid).addPost(new GPost("GossipGirl reply reply", "It Was TAL!!!!", elior, gossipThread));
                t_tid = GForum._theForum.getSubForum("News").GetThread(t_tid).addPost(new GPost("GossipGirl reply reply reply", "was it? ", gili, gossipThread));
                t_tid = GForum._theForum.getSubForum("News").GetThread(t_tid).addPost(new GPost("GossipGirl reply reply reply", "well I am not sure...", elior, gossipThread));
                GForum._theForum.getSubForum("News").publish(new GThread("Celebs", "Eyal was stuck outside of intel because his car...", elior));
                GForum._theForum.getSubForum("News").publish(new GThread("London2012", "Bolt is still the greatest runner of all times. yo yo ...", elior));
                GForum._theForum.getSubForum("News").publish(new GThread("Crime", "is horrible in beer sheba...", boris));
                GForum._theForum.getSubForum("News").publish(new GThread("Beer Sheba", "Layla Lavan was not so so", alex));
                GForum._theForum.getSubForum("News").publish(new GThread("BGU", "Why was the zoomba ...", gili));
                GForum._theForum.getSubForum("News").publish(new GThread("Salsa", "why are all the girls running away?!? shouldn't they be dancing for me?", boris));
                GForum._theForum.getSubForum("News").publish(new GThread("Apple", "New Mountain Lion OS upgrade ships today. and it is mind blowing.", alex));
                
                GForum._theForum.getSubForum("Cars").publish(new GThread("AUDI", "A8 is a bit more comfortable  then the A3, Worth the 134,000$ difference ?!?", eyal));
                GForum._theForum.getSubForum("Cars").publish(new GThread("KIA", "New Sportage wins beauty contest.", eyal));
                GForum._theForum.getSubForum("Cars").publish(new GThread("SUBARU", "B4 - to your FACE!!!", gili));
                GThread subaruThread = new GThread("SUBARU", "Did you know you should pronounce the U. like SUBAROO...", alex);
                t_tid = GForum._theForum.getSubForum("Cars").publish(subaruThread);
                GForum._theForum.getSubForum("Cars").GetThread(t_tid).addPost(new GPost("Language", "I believe it is up to local country definitions.", alex, gossipThread));
                GForum._theForum.getSubForum("Cars").publish(new GThread("BMW", "New S40 model now in stores. At only 140,000$.", boris));
                GForum._theForum.getSubForum("Cars").publish(new GThread("Pimp my ride", "my scooter no have plasma rings.", boris));
                GForum._theForum.getSubForum("Cars").publish(new GThread("Toyota", "Gili's Toyota car saved Eyal from his stuck event earlier today.", gili));

                GForum._theForum.getSubForum("Planes").publish(new GThread("Pilots","Gili rides her toyota like a jet. she should be a jet fighter pilot.", boris));
                GForum._theForum.getSubForum("Planes").publish(new GThread("ElAl", "Only 100$ for flying cargo to Egypt.", elior));
                GForum._theForum.getSubForum("Planes").publish(new GThread("IsraAir", "Eilat only 10$ per person per direction. (for orders before 29/08/12).", elior));
                GForum._theForum.getSubForum("Planes").publish(new GThread("Arkia",   "Eilat only 12$ per person per direction. (for orders before 22/08/12).", elior));
                GForum._theForum.getSubForum("Planes").publish(new GThread("Lufthansa", "Berlin only 99$ per person per direction. (for orders before 22/09/12).", elior));



                //GForum._theForum.getSubForums()[0].publish(new GThread("subject1", "content1",admin));
                //GThread th = GForum._theForum.getThreads(new GSubForum("Cars"))[0];
                //th.addPost(new GPost("post1", "post_content1", admin, th));

                //GPost p = th.getPosts()[0];
                //p.addPost(new GPost("post2", "post_content2", admin, th));
            }
            return GForum._theForum;
        }
    }
}
